package com.robot.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.robot.common.api.CurrentUserSupport;
import com.robot.common.enums.BulkFileStateEnum;
import com.robot.common.enums.IsDeleteEnum;
import com.robot.common.enums.KnowledgeTypeEnum;
import com.robot.common.result.PageResult;
import com.robot.common.utils.MinioUtils;
import com.robot.dao.dto.BulkFileDTO;
import com.robot.dao.dto.BulkfilePageQueryDTO;
import com.robot.dao.entity.BulkFile;
import com.robot.dao.entity.Knowledge;
import com.robot.dao.mapper.BulkFileMapper;
import com.robot.dao.mapper.KnowledgeMapper;
import com.robot.dao.vo.BulkfileVO;
import com.robot.dao.vo.FileOverViewVO;
import com.robot.service.BulkFileService;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;

@Slf4j
@Service
public class BulkFileServiceImpl extends ServiceImpl<BulkFileMapper, BulkFile> implements BulkFileService {
    //文件上传缓冲bucket
    private static final String BUFFER_ID = "1821091408614404171";
    private static final String BUFFER_BUCKET_NAME = "1274682200419205120";

    @Autowired
    private MinioUtils minioUtils;

    @Autowired
    private BulkFileMapper bulkFileMapper;

    @Autowired
    private KnowledgeMapper knowledgeMapper;

    @Autowired
    private CurrentUserSupport currentUserSupport;


    private final ThreadPoolTaskExecutor excelProcessingTaskExecutor;
    private final Map<String, Future<?>> taskMap = new ConcurrentHashMap<>();

    @Autowired
    public BulkFileServiceImpl(@Qualifier("excelProcessingTaskExecutor") ThreadPoolTaskExecutor taskExecutor) {
        this.excelProcessingTaskExecutor = taskExecutor;
    }

    /**
     * 删除文件（逻辑删除）
     */
    public void delete(Long fileId, Long knowledgeId) {
        //file表逻辑删除
        BulkFile file = new BulkFile();
        file.setId(fileId);
        file.setIsDelete(IsDeleteEnum.DELETED.getValue());
        super.updateById(file);

        Knowledge knowledge = knowledgeMapper.selectById(knowledgeId);
        knowledge.setFileNumber(knowledge.getFileNumber()-1);
        knowledgeMapper.updateById(knowledge);

    }

    /*
        创建BulkFile实体。注意，文件状态未设定。需要自行设定。
    */
    private BulkFile createBulkFile(BulkFileDTO bulkfileDTO, MultipartFile multipartFile) {
        BulkFile file = new BulkFile();
        file.setRobotid(Long.valueOf(bulkfileDTO.getRobotId()));
        file.setUserid(Long.valueOf(bulkfileDTO.getUserId()));
        file.setFileName(multipartFile.getOriginalFilename());
        file.setFileSize(String.valueOf(multipartFile.getSize()));
        file.setKnowledgeId(Long.valueOf(bulkfileDTO.getKnowledgeId()));
        file.setFileWordNumber(0);
        file.setFileUrl("null");
        file.setMinioBucketName(bulkfileDTO.getMinioBucketName());
        file.setIsDelete(IsDeleteEnum.EXIST.getValue());
        return file;
    }
    /*
    更新外链接。
     */
    private List<FileOverViewVO> updateFilesAndGetOverview(Map<Long, String> uploadResults, String bucketName, String knowledgeId,Map<Long, MultipartFile> multipartFiles ) {
        List<FileOverViewVO> files = new ArrayList<>();
        for (Map.Entry<Long, String> entry : uploadResults.entrySet()) {
            Long fileId = entry.getKey();
            String fileUrl = entry.getValue();

            BulkFile file = new BulkFile();
            file.setId(fileId);
            file.setFileUrl(fileUrl);
            file.setFileName(multipartFiles.get(fileId).getOriginalFilename());
            super.updateById(file);

            FileOverViewVO fileOverViewVO = new FileOverViewVO();
            BeanUtils.copyProperties(file, fileOverViewVO);
            fileOverViewVO.setFileId(String.valueOf(fileId));
            fileOverViewVO.setMinioBucketName(bucketName);
            fileOverViewVO.setKnowledgeId(knowledgeId);
            fileOverViewVO.setFileState(BulkFileStateEnum.Analysis.getCode());
            files.add(fileOverViewVO);

            Knowledge knowledge = knowledgeMapper.selectById(knowledgeId);
            knowledge.setFileNumber(knowledge.getFileNumber() + 1);
            knowledgeMapper.updateById(knowledge);
        }
        return files;
    }

    /**
     * 检查Excel文件格式是否正确
     */
    private boolean isValidExcelFormat(Sheet sheet) {
        if (sheet == null || sheet.getPhysicalNumberOfRows() == 0) {
            return false;
        }

        Row headerRow = sheet.getRow(0);
        if (headerRow == null || headerRow.getPhysicalNumberOfCells() < 2) {
            return false;
        }

        return true;
    }


    /**
     * 检查文件是否为Excel格式
     */
    private boolean isExcelFile(MultipartFile file) {
        // 获取文件的内容类型
        String contentType = file.getContentType();

        // 获取文件名的后缀
        String fileName = file.getOriginalFilename();

        // 检查内容类型是否为Excel格式
        boolean isExcelContentType = contentType != null &&
                (contentType.equals("application/vnd.ms-excel") ||
                        contentType.equals("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));

        // 检查文件名后缀是否为Excel格式
        boolean isExcelExtension = fileName != null &&
                (fileName.endsWith(".xls") || fileName.endsWith(".xlsx"));

        // 只要满足内容类型或者文件后缀之一为Excel格式即可
        return isExcelContentType || isExcelExtension;
    }


    @Transactional
    @ApiOperation(value = "上传批量回答文件")
    public List<FileOverViewVO> uploadBulkFile(BulkFileDTO bulkfileDTO) throws IOException {

        String bucketName = null;
        Long knowledgeId = null;
        if (Objects.equals(bulkfileDTO.getKnowledgeId(), "0")) {//没有专属知识库，先丢到缓冲区
            bucketName = BUFFER_BUCKET_NAME;
            knowledgeId = Long.valueOf(BUFFER_ID);
        } else {
            //检查桶（知识库）是否存在，不存在则创建
            bucketName = minioUtils.existBucketPartial(bulkfileDTO.getMinioBucketName());
            knowledgeId = Long.valueOf(bulkfileDTO.getKnowledgeId());
        }
        bulkfileDTO.setKnowledgeId(String.valueOf(knowledgeId));
        bulkfileDTO.setMinioBucketName(bucketName);

        Map<Long, MultipartFile> multipartFiles = new HashMap<>();
        //将信息写入数据库
        for (MultipartFile multipartFile : bulkfileDTO.getFiles()) {
            BulkFile file = createBulkFile(bulkfileDTO, multipartFile);
            file.setFileState(BulkFileStateEnum.Analysis.getCode());
            super.save(file);
            //minio中存储的文件名（主键ID_fileName）
            multipartFiles.put(file.getId(), multipartFile);
        }
        Map<Long, String> upload = minioUtils.upload(bucketName, multipartFiles);

        //更新文件外链
        List<FileOverViewVO> files = new ArrayList<>();
        files=updateFilesAndGetOverview(upload,bulkfileDTO.getMinioBucketName(), bulkfileDTO.getKnowledgeId(),multipartFiles);
        return files;
    }

    @Transactional
    @ApiOperation(value = "根据文件id，用户id，机器人id查批量文件")
    public BulkFile findFileByIds(String fileId, String robotId, String userId) {
        QueryWrapper<BulkFile> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", fileId)
                .eq("robotid", robotId)
                .eq("userid", userId);
        BulkFile bulkFile =super.getOne(queryWrapper);
        LocalDateTime updateTime = bulkFile.getUpdateTime();
        long between = ChronoUnit.DAYS.between(updateTime, LocalDateTime.now());
        if(between>=7){//文件外链接过期,获取新链接
            String minioFileName = bulkFile.getId() + "-" + bulkFile.getFileName();
            String fileUrl = minioUtils.getPresignedObjectUrl(bulkFile.getMinioBucketName(), minioFileName);
            bulkFile.setFileUrl(fileUrl);
            this.updateById(bulkFile);
        }
        return bulkFile;
    }


    // 完整保存已处理的数据
    private void saveProcessedData(Workbook workbook, BulkFile oribulkFile, String uniqueFileName, boolean isInterrupted) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        workbook.write(outputStream);
        workbook.close();

        String bucketName = null;
        Long knowledgeId = null;
        if (Objects.equals(oribulkFile.getKnowledgeId(), "0")) {//没有专属知识库，先丢到缓冲区
            bucketName = BUFFER_BUCKET_NAME;
            knowledgeId = Long.valueOf(BUFFER_ID);
        } else {
            //检查桶（知识库）是否存在，不存在则创建
            bucketName = minioUtils.existBucketPartial(oribulkFile.getMinioBucketName());
            knowledgeId = oribulkFile.getKnowledgeId();
        }

        MultipartFile processedFile = new MockMultipartFile(
                uniqueFileName,
                oribulkFile.getFileName(),
                "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                outputStream.toByteArray()
        );
        // 保存处理后的Excel到数据库
        BulkFileDTO bulkFileDTO = new BulkFileDTO();
        bulkFileDTO.setRobotId(String.valueOf(oribulkFile.getRobotid()));
        bulkFileDTO.setUserId(String.valueOf(oribulkFile.getUserid()));
        bulkFileDTO.setMinioBucketName(bucketName);
        bulkFileDTO.setKnowledgeId(String.valueOf(knowledgeId));
        BulkFile bulkFile  = createBulkFile(bulkFileDTO,processedFile);
        bulkFile.setCreateTime(oribulkFile.getCreateTime());
        if (!isInterrupted) {
            bulkFile.setFileState(BulkFileStateEnum.Success.getCode());
        }else {
            bulkFile.setFileState(BulkFileStateEnum.Fail.getCode());
        }
        super.save(bulkFile);

        delete(oribulkFile.getId(),oribulkFile.getKnowledgeId());
        // 上传处理后的Excel文件到MinIO
        Map<Long, MultipartFile> multipartFiles = new HashMap<>();
        multipartFiles.put(bulkFile.getId(), processedFile);
        Map<Long, String> uploadResult = minioUtils.upload(bulkFile.getMinioBucketName(), multipartFiles);
        List<FileOverViewVO> files = new ArrayList<>();
        files=updateFilesAndGetOverview(uploadResult,bulkFile.getMinioBucketName(), String.valueOf(bulkFile.getKnowledgeId()),multipartFiles);

    }


    @Transactional
    @ApiOperation(value = "批量回答")
    public String processFile(BulkFile oribulkFile,MultipartFile file) {

        String uniqueFileName = String.valueOf(oribulkFile.getId());
        if (!isExcelFile(file)) {
            throw new IllegalArgumentException("上传的文件不是Excel格式");
        }
        Future<?> future = excelProcessingTaskExecutor.submit(() -> {
            try {

                // 用于存储处理状态的标志
                boolean isInterrupted = false;
                // 打开Excel文件
                Workbook workbook = new XSSFWorkbook(file.getInputStream());
                Sheet sheet = workbook.getSheetAt(0);


                if (!isValidExcelFormat(sheet)) {
                    throw new IllegalArgumentException("Excel文件格式不正确，文件应至少包含两列：问题和回答");
                }

                // 逐行扫描并调用ChatGPT（此处简化为填入固定答案）
                for (Row row : sheet) {
                    if (row.getRowNum() == 0) continue; // 跳过标题行

                    String question = row.getCell(0).getStringCellValue();
                    //todo 还未调用chatgpt
                    String answer = "ChatGPT回答：" + question; // 这里调用ChatGPT API


                    row.createCell(1).setCellValue(answer);

                    // 检查任务是否被取消
                    if (Thread.currentThread().isInterrupted()) {
                        Thread.interrupted();
                        isInterrupted = true; // 标记任务被中断
                        break;
                    }

                }

                // 保存处理后的Excel

                saveProcessedData(workbook,oribulkFile,uniqueFileName,isInterrupted);

                if (isInterrupted){
                    throw new InterruptedException("任务被中断");
                }

            }catch (IOException e) {
                log.error("文件处理出现IO异常", e);
            } catch (InterruptedException e) {
                // 处理异常
                Thread.currentThread().interrupt();
                log.info("任务被中断，处理中止");
                // 这里可以记录中断日志或采取其他措施
            } finally {
                taskMap.remove(uniqueFileName); // 无论成功或失败，任务完成后都从任务管理中移除
            }
        });

        taskMap.put(uniqueFileName, future);
        return uniqueFileName;
    }

    @Transactional
    @ApiOperation(value = "中断批量回答")
    public boolean stopTask(String fileId) {
        Future<?> future = taskMap.get(fileId);
        if (future != null) {
            boolean cancelled = future.cancel(true); // 请求中断
            if (cancelled) {
                log.info("响应中断");
                taskMap.remove(fileId); // 从任务列表中移除任务
                BulkFile bulkFile = new BulkFile();
                bulkFile.setFileState(BulkFileStateEnum.Fail.getCode());
                bulkFile.setId(Long.valueOf(fileId));
                super.updateById(bulkFile);
                return true;
            }
        }
        return false;
    }

    @Transactional
    @ApiOperation(value = "分页查询批量文件")
    public PageResult pageQueryfile(BulkfilePageQueryDTO bulkFilePageQueryDTO){
        String userId= String.valueOf(currentUserSupport.getCurrentUserId());
        List<Knowledge> knowledges = knowledgeMapper.queryByRobotIdAndKnowtype(Long.valueOf(bulkFilePageQueryDTO.getRobotId()), KnowledgeTypeEnum.BATCH_UPLOAD.getCode());
        if ( knowledges.size()==0){
            return new PageResult(0,false,new ArrayList<>());
        }

        Long knowledgeId=knowledges.get(0).getId();
        PageHelper.startPage(bulkFilePageQueryDTO.getPage(), bulkFilePageQueryDTO.getPageSize());
        Page<BulkFile> page = bulkFileMapper.pageQueryFile(knowledgeId, userId);

        boolean hasMore = false;
        long total = page.getTotal();
        Long totalPages = (long) Math.ceil(total / bulkFilePageQueryDTO.getPageSize());
        if(total % bulkFilePageQueryDTO.getPageSize()>0){
            totalPages = totalPages+1;
        }
        if(totalPages != bulkFilePageQueryDTO.getPage()){
            hasMore = true;
        }

        List<BulkfileVO> list = new ArrayList<>();
        for (BulkFile file : page) {
            BulkfileVO fileVO = new BulkfileVO();
            BeanUtils.copyProperties(file, fileVO);
            fileVO.setFileId(String.valueOf(file.getId()));
            list.add(fileVO);
        }
        return new PageResult(page.getTotal(),hasMore,list);
    }

}
